package vivace.helper;

import javax.sound.midi.Instrument;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;

import vivace.exceptions.MidiMessageNotFoundException;

import java.util.Vector;

public class ProjectHelper {

	/**
	 * Returns the name and octave of the note with the specified number
	 * Specification found at http://www.harmony-central.com/MIDI/Doc/table2.html
	 */
	public static String getNoteName( int note, boolean displayOctave ){
		
		String s = "";
		
		if( note < 0 || note > 127 )
			return "N/A";
		
		// Get the note
		switch (note % 12){
			case 0: s = "C"; break;
			case 1: s = "C#"; break;
			case 2: s = "D"; break;
			case 3: s = "D#"; break;
			case 4: s = "E"; break;
			case 5: s = "F"; break;
			case 6: s = "F#"; break;
			case 7: s = "G"; break;
			case 8: s = "G#"; break;
			case 9: s = "A"; break;
			case 10: s = "A#"; break;
			case 11: s = "B"; break;
		}

		// Get the octave
		int octave = (int) Math.ceil(note / 12) - 1;
		return s + (displayOctave ? " " + octave : "");
	}
	
	/**
	 * Converts an unsigned byte to a integer value
	 */
	public static int byteToInt(byte b) {
	    return (int) b & 0xFF;
	
	}

	/**
	 * Converts an unsigned byte to a hexadecimal value
	 */
	public static String byteToHex(byte b){
		int i = b & 0xFF;
		return Integer.toHexString(i);
	}
	
	/**
	 * Returns the first meta message of the given type in the given track
	 * or null if no matching meta messages were found.
	 * @param track
	 * @param type
	 * @return
	 */
	public static MetaMessage filterFirstMetaMessage(Track track, int type)
			throws MidiMessageNotFoundException {
		Vector<MidiEvent> metaMessageEvents = filterMetaMessages(track, type);
		
		if(metaMessageEvents.size() > 0) {
			return (MetaMessage) metaMessageEvents.firstElement().getMessage();
		} else {
			throw new MidiMessageNotFoundException();
		}
	}
	
	/**
	 * Returns the first short message in the given track of the given command type
	 * or null if no matching short messages were found.
	 * @param track
	 * @param command
	 * @return
	 */
	public static ShortMessage filterFirstShortMessage(Track track, int command) 
			throws MidiMessageNotFoundException {
		
		Vector<MidiEvent> shortMessageEvents = filterShortMessages(track, command);
		
		if (shortMessageEvents.size() > 0) {
			return (ShortMessage) shortMessageEvents.firstElement().getMessage();
		} else {
			throw new MidiMessageNotFoundException();
		}
		
	}
	
	/**
	 * Returns the first meta message of the track
	 * @param track
	 * @return
	 */
	public static MetaMessage firstMetaMessage(Track track) 
			throws MidiMessageNotFoundException {
		Vector<MidiEvent> metaMessageEvents = allMetaMessages(track);
		
		if(metaMessageEvents.size() > 0) {
			return (MetaMessage) metaMessageEvents.firstElement().getMessage();
		} else {
			throw new MidiMessageNotFoundException();
		}
	}
	
	/**
	 * Returns the first short message of the track
	 * @param track
	 * @return
	 */
	public static ShortMessage firstShortMessage(Track track) 
			throws MidiMessageNotFoundException {
		Vector<MidiEvent> shortMessageEvents = allShortMessages(track);
		
		if (shortMessageEvents.size() > 0) {
			return (ShortMessage) shortMessageEvents.firstElement().getMessage();
		} else {
			throw new MidiMessageNotFoundException();
		}
	}
	
	/**
	 * Returns all the MidiEventS containing MetaMessageS of the specified type in the given track 
	 * @param track
	 * @param type
	 * @return
	 */
	public static Vector<MidiEvent> filterMetaMessages(Track track, int type) {
		
		Vector<MidiEvent> events = new Vector<MidiEvent>();
		
		int i = 0;
		MidiEvent e;
		MidiMessage m;
		
		do {
			try {
				e = track.get(i);
			} catch (ArrayIndexOutOfBoundsException ex) {
				return events;
			}
			
			m = e.getMessage();
			
			if (m instanceof MetaMessage) {
				MetaMessage mm = (MetaMessage) m;
				
				if (mm.getType() == type) {
					events.add(e);
				} 
			}
			
			i++;
			
		} while (e != null);
		
		return events;
	}
	
	/**
	 * Returns all the MidiEventS containing ShortMessageS of the specified command in the given track 
	 * @param track
	 * @param command
	 * @return
	 */
	public static Vector<MidiEvent> filterShortMessages(Track track, int command) {
		Vector<MidiEvent> events = new Vector<MidiEvent>();
		
		MidiEvent e;
		MidiMessage m;
		int i = 0;
		
		do {
			try {
				e = track.get(i);
			} catch (ArrayIndexOutOfBoundsException ex) {
				return events;
			}
			
			m = e.getMessage();
			
			if (m instanceof ShortMessage) {
				ShortMessage sm = (ShortMessage) m;
				
				if (sm.getCommand() == command) {
					
					events.add(e);
					
				} 
			}
			
			i++;
			
		} while (e != null);
		
		return events;
	}
	
	/**
	 * Returns all MetaMessageS in the given track 
	 * as a vector of MidiEventS 
	 * @param track
	 * @return
	 */
	public static Vector<MidiEvent> allMetaMessages(Track track) {
		
		Vector<MidiEvent> events = new Vector<MidiEvent>();
		
		int i = 0;
		MidiEvent e;
		MidiMessage m;
		
		do {
			try {
				e = track.get(i);
			} catch (ArrayIndexOutOfBoundsException ex) {
				return events;
			}
			
			m = e.getMessage();
			
			if (m instanceof MetaMessage) {

				events.add(e);
				
			}
			
			i++;
			
		} while (e != null);
		
		return events;
	}
	
	/**
	 * Returns all ShortMessageS in the given track 
	 * as a vector of MidiEventS 
	 * @param track
	 * @return
	 */
	public static Vector<MidiEvent> allShortMessages(Track track) {
		Vector<MidiEvent> events = new Vector<MidiEvent>();
		
		MidiEvent e;
		MidiMessage m;
		int i = 0;
		
		do {
			try {
				e = track.get(i);
			} catch (ArrayIndexOutOfBoundsException ex) {
				return events;
			}
			
			m = e.getMessage();
			
			if (m instanceof ShortMessage) {
						
				events.add(e);
			
			}
			
			i++;
			
		} while (e != null);
		
		return events;
	}
}
